﻿using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Infrastructure;
using CID.Tests.Support;
using NUnit.Framework;
using Rhino.Mocks;

namespace CID.Domain.Tests.Infrastructure
{
    [TestFixture]
    public class ActivationDeactivationDefinitionTests : BaseFunctionalTests
    {
        private ActivationDeactivationDefinition definition;
        private IParameter[] activationParameters, deactivationParameters;

        public override void SetUp()
        {
            base.SetUp();
            var activationTemplate = StubsBuilder.BuildActivationTemplateStub();
            var deactivationTemplate = StubsBuilder.BuildDeactivationTemplateStub();

            var activationParameter1 = StubsBuilder.BuildParameterStub(template: activationTemplate);
            var activationParameter2 = StubsBuilder.BuildParameterStub(template: activationTemplate);
            var activationParameter3 = StubsBuilder.BuildParameterStub(template: activationTemplate);
            activationParameters = new IParameter[] { activationParameter1, activationParameter2, activationParameter3 };
            activationTemplate.Stub(x => x.Parameters).Return(activationParameters);

            var deactivationParameter1 = StubsBuilder.BuildParameterStub(template: deactivationTemplate);
            var deactivationParameter2 = StubsBuilder.BuildParameterStub(template: deactivationTemplate);
            var deactivationParameter3 = StubsBuilder.BuildParameterStub(template: deactivationTemplate);
            deactivationParameters = new IParameter[] { deactivationParameter1, deactivationParameter2, deactivationParameter3 };
            deactivationTemplate.Stub(x => x.Parameters).Return(deactivationParameters);

            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            definition.ActivationTemplate = activationTemplate;
            definition.DeactivationTemplate = deactivationTemplate;
        }

        #region Constructor
        [Test]
        public void CreateActivationDeactivationDefinition_FreshComponent_ShouldSucceed()
        {
            var component = Development.StubsBuilder.BuildComponentStub();

            var definition = new ActivationDeactivationDefinition(component);

            Assert.AreEqual(component, definition.Component);
        }

        [Test, ExpectedException(typeof(ParentAlreadyAssignedException))]
        public void CreateActivationDeactivationDefinition_ComponentWithActivationDeactivationDefinitionAssigned_ShouldThrowException()
        {
            var component = Development.StubsBuilder.BuildComponentStub(activationDeactivationDefinition: Infrastructure.StubsBuilder.BuildActivationDeactivationDefinition());

            var definition = new ActivationDeactivationDefinition(component);
        }
        #endregion

        #region AddEnvironmentParameterValue
        [Test, ExpectedException(typeof(InvalidParameterValueDefinitionException))]
        public void AddEnvironmentParameterValue_ParameterValueWithParameterAssignedToAnotherTemplate_ShouldThrowException()
        {
            var template = StubsBuilder.BuildTemplateStub();
            var parameter = StubsBuilder.BuildParameterStub(template: template);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            definition.AddEnvironmentParameterValue(parameter, environment);
        }

        [Test]
        public void AddEnvironmentParameterValue_ParameterValueWithParameterAssignedToActivationTemplate_ShouldSucced()
        {
            var defaultParameterValuesCount = definition.DefaultParameterValues.Count();
            var parameter = StubsBuilder.BuildParameterStub(template: definition.ActivationTemplate);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            var parameterValue = definition.AddEnvironmentParameterValue(parameter, environment);

            Assert.AreEqual(definition, parameterValue.Definition);
            Assert.AreEqual(parameter, parameterValue.Parameter);
            Assert.AreEqual(environment, parameterValue.Environment);
            Assert.AreEqual(defaultParameterValuesCount + 1, definition.ParameterValues.Count());
            Assert.AreEqual(1, definition.EnvironmentParameterValues.Count());
            CollectionAssert.Contains(definition.ParameterValues, parameterValue);
            CollectionAssert.Contains(definition.EnvironmentParameterValues, parameterValue);
        }

        [Test]
        public void AddEnvironmentParameterValue_ParameterValueWithParameterAssignedToDeactivationTemplate_ShouldSucced()
        {
            var defaultParameterValuesCount = definition.DefaultParameterValues.Count();
            var parameter = StubsBuilder.BuildParameterStub(template: definition.DeactivationTemplate);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            var parameterValue = definition.AddEnvironmentParameterValue(parameter, environment);

            Assert.AreEqual(definition, parameterValue.Definition);
            Assert.AreEqual(parameter, parameterValue.Parameter);
            Assert.AreEqual(environment, parameterValue.Environment);
            Assert.AreEqual(defaultParameterValuesCount + 1, definition.ParameterValues.Count());
            Assert.AreEqual(1, definition.EnvironmentParameterValues.Count());
            CollectionAssert.Contains(definition.ParameterValues, parameterValue);
            CollectionAssert.Contains(definition.EnvironmentParameterValues, parameterValue);
        }
        #endregion

        #region GetActivationParameterValues
        [Test]
        public void GetActivationParameterValues_EnvironmentParameterValuesAdded_ShouldReturnAllEnvironmentParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(activationParameters[0], environment);
            var parameterValue2 = definition.AddEnvironmentParameterValue(activationParameters[1], environment);
            var parameterValue3 = definition.AddEnvironmentParameterValue(activationParameters[2], environment);

            var parameterValues = definition.GetActivationParamaterValues(environment);

            Assert.AreEqual(activationParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IEnvironmentParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetActivationParameterValues_NoEnvironmentParameterValuesAdded_ShouldReturnAllDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.GetDefaultParameterValue(activationParameters[0]);
            var parameterValue2 = definition.GetDefaultParameterValue(activationParameters[1]);
            var parameterValue3 = definition.GetDefaultParameterValue(activationParameters[2]);

            var parameterValues = definition.GetActivationParamaterValues(environment);

            Assert.AreEqual(activationParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IDefaultParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetActivationParameterValues_SomeEnvironmentParameterValuesNotAdded_ShouldReturnMixedEnvironmentAndDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(activationParameters[0], environment);
            var parameterValue2 = definition.GetDefaultParameterValue(activationParameters[1]);
            var parameterValue3 = definition.AddEnvironmentParameterValue(activationParameters[2], environment);

            var parameterValues = definition.GetActivationParamaterValues(environment);

            Assert.AreEqual(activationParameters.Count(), parameterValues.Count());
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }
        #endregion

        #region GetDeactivationParameterValues
        [Test]
        public void GetDeactivationParameterValues_EnvironmentParameterValuesAdded_ShouldReturnAllEnvironmentParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(deactivationParameters[0], environment);
            var parameterValue2 = definition.AddEnvironmentParameterValue(deactivationParameters[1], environment);
            var parameterValue3 = definition.AddEnvironmentParameterValue(deactivationParameters[2], environment);

            var parameterValues = definition.GetDeactivationParamaterValues(environment);

            Assert.AreEqual(deactivationParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IEnvironmentParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetDeactivationParameterValues_NoEnvironmentParameterValuesAdded_ShouldReturnAllDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.GetDefaultParameterValue(deactivationParameters[0]);
            var parameterValue2 = definition.GetDefaultParameterValue(deactivationParameters[1]);
            var parameterValue3 = definition.GetDefaultParameterValue(deactivationParameters[2]);

            var parameterValues = definition.GetDeactivationParamaterValues(environment);

            Assert.AreEqual(deactivationParameters.Count(), parameterValues.Count());
            CollectionAssert.AllItemsAreInstancesOfType(parameterValues, typeof(IDefaultParameterValue));
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }

        [Test]
        public void GetDeactivationParameterValues_SomeEnvironmentParameterValuesNotAdded_ShouldReturnMixedEnvironmentAndDefaultParameters()
        {
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameterValue1 = definition.AddEnvironmentParameterValue(deactivationParameters[0], environment);
            var parameterValue2 = definition.GetDefaultParameterValue(deactivationParameters[1]);
            var parameterValue3 = definition.AddEnvironmentParameterValue(deactivationParameters[2], environment);

            var parameterValues = definition.GetDeactivationParamaterValues(environment);

            Assert.AreEqual(deactivationParameters.Count(), parameterValues.Count());
            CollectionAssert.AreEquivalent(new IParameterValue[] { parameterValue1, parameterValue2, parameterValue3 }, parameterValues);
        }
        #endregion

        #region ActivationTemplate
        [Test]
        public void AssignActivationTemplate_ActivationTemplateNotAssigned_ShouldSetDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var activationTemplate = StubsBuilder.BuildActivationTemplateStubWithAllParameterTypes();

            definition.ActivationTemplate = activationTemplate;

            Assert.AreEqual(activationTemplate.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in activationTemplate.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void AssignActivationTemplate_ActivationTemplatePreviouslyAssigned_ShouldClearPreviousAssignedParameterValuesAndSetNewDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var activationTemplate1 = StubsBuilder.BuildActivationTemplateStubWithAllParameterTypes();
            definition.ActivationTemplate = activationTemplate1;
            definition.AddEnvironmentParameterValue(activationTemplate1.Parameters.First(), environment);
            var activationTemplate2 = StubsBuilder.BuildActivationTemplateStubWithAllParameterTypes();

            definition.ActivationTemplate = activationTemplate2;

            Assert.AreEqual(activationTemplate2.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in activationTemplate1.Parameters)
                Assert.IsNull(definition.GetDefaultParameterValue(parameter));
            foreach (var parameter in activationTemplate2.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void UnassignActivationTemplate_ActivationTemplateAssigned_ShouldClearDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var activationTemplate = StubsBuilder.BuildActivationTemplateStubWithAllParameterTypes();
            definition.ActivationTemplate = activationTemplate;

            definition.ActivationTemplate = null;

            CollectionAssert.IsEmpty(definition.ParameterValues.ToArray());
        }
        #endregion

        #region DeactivationTemplate
        [Test]
        public void AssignDeactivationTemplate_DeactivationTemplateNotAssigned_ShouldSetDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var deactivationTemplate = StubsBuilder.BuildDeactivationTemplateStubWithAllParameterTypes();

            definition.DeactivationTemplate = deactivationTemplate;

            Assert.AreEqual(deactivationTemplate.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in deactivationTemplate.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void AssignDeactivationTemplate_DeactivationTemplatePreviouslyAssigned_ShouldClearPreviousAssignedParameterValuesAndSetNewDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            var deactivationTemplate1 = StubsBuilder.BuildDeactivationTemplateStubWithAllParameterTypes();
            definition.DeactivationTemplate = deactivationTemplate1;
            definition.AddEnvironmentParameterValue(deactivationTemplate1.Parameters.First(), environment);
            var deactivationTemplate2 = StubsBuilder.BuildDeactivationTemplateStubWithAllParameterTypes();

            definition.DeactivationTemplate = deactivationTemplate2;

            Assert.AreEqual(deactivationTemplate2.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in deactivationTemplate1.Parameters)
                Assert.IsNull(definition.GetDefaultParameterValue(parameter));
            foreach (var parameter in deactivationTemplate2.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }

        [Test]
        public void UnassignDeactivationTemplate_DeactivationTemplateAssigned_ShouldClearDefaultParameterValues()
        {
            definition = new ActivationDeactivationDefinition(Development.StubsBuilder.BuildComponentStub());
            var deactivationTemplate = StubsBuilder.BuildDeactivationTemplateStubWithAllParameterTypes();
            definition.DeactivationTemplate = deactivationTemplate;

            definition.DeactivationTemplate = null;

            CollectionAssert.IsEmpty(definition.ParameterValues.ToArray());
        }
        #endregion
    }
}
